package com.hmy.finance.service.receipt.impl;

import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.hmy.ccp.error.exception.HMYRuntimeException;
import com.hmy.finance.api.common.enums.receipt.AccountTradeBusinessTypeEnum;
import com.hmy.finance.api.common.enums.receipt.InsufficientExpensesMethodEnum;
import com.hmy.finance.api.common.enums.receipt.OffsetStatusEnum;
import com.hmy.finance.common.constants.CommonConstants;
import com.hmy.finance.common.enums.PaymentStatusEnum;
import com.hmy.finance.dal.mapper.DeviceDailyBillMapper;
import com.hmy.finance.dal.mapper.ProfitLossRecordMapper;
import com.hmy.finance.dal.po.DeviceDailyBillPo;
import com.hmy.finance.dal.po.ProfitLossRecordPo;
import com.hmy.finance.dal.po.receipt.ContractOffsetRecordPo;
import com.hmy.finance.dal.po.receipt.json.OffsetBillDetailInfo;
import com.hmy.finance.service.core.IFinancialCoreDataService;
import com.hmy.finance.service.core.dto.RefreshContractAccountOfAgeDto;
import com.hmy.finance.service.core.rdto.RefreshContractAccountOfAgeRdto;
import com.hmy.finance.service.receipt.ContractOffsetRecordService;
import com.hmy.finance.service.receipt.IncomeExpensesService;
import com.hmy.finance.service.receipt.dto.contactorAccount.ContactorExpensesDto;
import com.hmy.finance.service.receipt.dto.contactorAccount.ContactorIncomeDto;
import com.hmy.finance.service.receipt.rdto.TradeRdto;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

@Slf4j
@Service
public class OffsetServiceImpl {
    @Resource
    private ContractOffsetRecordService contractOffsetRecordService;
    @Resource
    private IFinancialCoreDataService financialCoreDataService;
    @Resource
    private IncomeExpensesService incomeExpensesService;
    @Resource
    private DeviceDailyBillMapper deviceDailyBillMapper;
    @Resource
    private ProfitLossRecordMapper profitLossRecordMapper;

    //        @Async
    public void offsetManualContractBill(Long receiptOffsetBillId) {
        List<ContractOffsetRecordPo> waitOffsetContractRecordList = contractOffsetRecordService.list(
                Wrappers.lambdaQuery(ContractOffsetRecordPo.class)
                        .eq(ContractOffsetRecordPo::getReceiptOffsetBillId, receiptOffsetBillId)
                        .eq(ContractOffsetRecordPo::getOffsetStatus, OffsetStatusEnum.WAIT)
                        .eq(ContractOffsetRecordPo::getIsDeleted, CommonConstants.NOT_DELETED)
        );
        //loop waitOffsetContractRecordList to offset
        for (ContractOffsetRecordPo contractOffsetRecordPo : waitOffsetContractRecordList) {
            if (contractOffsetRecordPo.getAmount().equals(BigDecimal.ZERO)) {
                log.info("核销金额为0:{}", contractOffsetRecordPo.getId());
                continue;

            }
            TradeRdto offset = offset(contractOffsetRecordPo);
            if (!offset.isSuccess()) {
                log.error("核销失败:{}", contractOffsetRecordPo.getId());
                throw new HMYRuntimeException("核销失败");
            } else {
                log.info("核销成功:{}", contractOffsetRecordPo.getId());
                RefreshContractAccountOfAgeDto refreshDto = new RefreshContractAccountOfAgeDto();
                refreshDto.setContractIdList(Collections.singletonList(contractOffsetRecordPo.getContractId()));
                RefreshContractAccountOfAgeRdto refreshContractAccountOfAgeRdto = financialCoreDataService.refreshContractAccountOfAge(refreshDto);
                if (!refreshContractAccountOfAgeRdto.isAllSuccess()) {
                    log.error("刷新合同账龄失败:{}", contractOffsetRecordPo.getId());
//                    throw new HMYRuntimeException("刷新合同账龄失败");
                }
            }
        }
    }

    @Transactional(rollbackFor = Exception.class)
    public TradeRdto offset(ContractOffsetRecordPo contractOffsetRecordPo) {
        TradeRdto tradeRdto = null;
        if (contractOffsetRecordPo.getAmount().compareTo(BigDecimal.ZERO) > 0) {
            ContactorExpensesDto contactorExpensesDto = new ContactorExpensesDto();
            contactorExpensesDto.setContactorId(contractOffsetRecordPo.getContactorId());
            contactorExpensesDto.setAmount(contractOffsetRecordPo.getAmount().abs());
            contactorExpensesDto.setBusinessType(AccountTradeBusinessTypeEnum.AUTO);
            contactorExpensesDto.setBusinessBillId(contractOffsetRecordPo.getContractId());
            contactorExpensesDto.setFeeIncurredTime(contractOffsetRecordPo.getFeeIncurredTime());
            contactorExpensesDto.setInsufficientExpensesMethod(InsufficientExpensesMethodEnum.PARTIALLY);
            // 3.1 expenses balance from contactor account
            tradeRdto = incomeExpensesService.expensesBalance(contactorExpensesDto);
        } else if (contractOffsetRecordPo.getAmount().compareTo(BigDecimal.ZERO) < 0) {
            ContactorIncomeDto contactorIncomeDto = new ContactorIncomeDto();
            contactorIncomeDto.setCustomerContactorId(contractOffsetRecordPo.getContactorId());
            contactorIncomeDto.setAmount(contractOffsetRecordPo.getAmount().abs());
            contactorIncomeDto.setBusinessType(AccountTradeBusinessTypeEnum.AUTO);
            contactorIncomeDto.setBusinessBillId(contractOffsetRecordPo.getContractId());
            contactorIncomeDto.setFeeIncurredTime(contractOffsetRecordPo.getFeeIncurredTime());
            // 3.1 expenses balance from contactor account
            tradeRdto = incomeExpensesService.incomeBalance(contactorIncomeDto);
        }

        if (tradeRdto.isSuccess()) {
            // 3.2 if tradeRdto.getAmount() equals to offsetContractDailyFeeBillDto.getOffsetTotalAmount(),set offsetStatus to ALL
            if (tradeRdto.getAmount().compareTo(contractOffsetRecordPo.getAmount()) == 0) {
                contractOffsetRecordPo.setOffsetStatus(OffsetStatusEnum.ALL);
            } else if (tradeRdto.getAmount().compareTo(BigDecimal.ZERO) > 0) {
                // 3.3 else set offsetStatus to PARTIALLY
                contractOffsetRecordPo.setOffsetStatus(OffsetStatusEnum.PARTIALLY);
            } else {
                contractOffsetRecordPo.setOffsetStatus(OffsetStatusEnum.WAIT);
            }
            contractOffsetRecordPo.setContactorTradeId(tradeRdto.getTradeId());
            contractOffsetRecordPo.setActualOffsetAmount(tradeRdto.getAmount());
            // 3.4 loop contractOffsetRecordPo.getOffsetBillDetailInfos() and set Actual Offset Amount
            BigDecimal actualOffsetTotalAmount = tradeRdto.getAmount();
            List<ProfitLossRecordPo> waitOffsetReceivableAdjustProfitLossInfoList = new ArrayList<>();
            List<DeviceDailyBillPo> waitOffsetDeviceDailyBillInfoList = new ArrayList<>();
            handleBusinessBillOffset(contractOffsetRecordPo, actualOffsetTotalAmount, waitOffsetReceivableAdjustProfitLossInfoList, waitOffsetDeviceDailyBillInfoList);

            // 4. Update ContractOffsetRecordPo
            if (!contractOffsetRecordService.updateById(contractOffsetRecordPo)) {
                throw new HMYRuntimeException(506700001);
            }
            // 5. Update Business Bill Offset Result
            if (!waitOffsetReceivableAdjustProfitLossInfoList.isEmpty()) {
                int updateResult = profitLossRecordMapper.batchUpdateOffsetResult(waitOffsetReceivableAdjustProfitLossInfoList);
                if (updateResult < 0) {
                    throw new HMYRuntimeException(506700001);
                }
            }
            if (!waitOffsetDeviceDailyBillInfoList.isEmpty()) {
                int updateResult2 = deviceDailyBillMapper.batchUpdateOffsetResult(waitOffsetDeviceDailyBillInfoList);
                if (updateResult2 < 0) {
                    throw new HMYRuntimeException(506700001);
                }
            }
        }
        return tradeRdto;
    }

    private void handleBusinessBillOffset(ContractOffsetRecordPo contractOffsetRecordPo, BigDecimal actualOffsetTotalAmount, List<ProfitLossRecordPo> waitOffsetReceivableAdjustProfitLossInfoList, List<DeviceDailyBillPo> waitOffsetDeviceDailyBillInfoList) {
        for (OffsetBillDetailInfo offsetBillDetailInfo : contractOffsetRecordPo.getOffsetBillDetailInfos()) {
            // if actualOffsetTotalAmount greater than or equals offsetBillDetailInfo.getBillTotalAmount(), set offsetBillDetailInfo's Actual Offset Amount to offsetBillDetailInfo.getBillTotalAmount()
            if (actualOffsetTotalAmount.compareTo(offsetBillDetailInfo.getBillTotalAmount()) >= 0) {
                offsetBillDetailInfo.setOffsetStatus(3);
                offsetBillDetailInfo.setActualOffsetAmount(offsetBillDetailInfo.getBillTotalAmount());
                actualOffsetTotalAmount = actualOffsetTotalAmount.subtract(offsetBillDetailInfo.getBillTotalAmount());
            } else if (actualOffsetTotalAmount.compareTo(BigDecimal.ZERO) > 0) {
                offsetBillDetailInfo.setOffsetStatus(2);
                // else set offsetBillDetailInfo's Actual Offset Amount to actualOffsetTotalAmount
                offsetBillDetailInfo.setActualOffsetAmount(actualOffsetTotalAmount);
                actualOffsetTotalAmount = actualOffsetTotalAmount.subtract(actualOffsetTotalAmount);
            } else {
                offsetBillDetailInfo.setOffsetStatus(1);
                offsetBillDetailInfo.setActualOffsetAmount(BigDecimal.ZERO);
            }
            // 3.5 Handle Business Bill Offset Result
            if (offsetBillDetailInfo.getOffsetStatus() > 1) {
                if (offsetBillDetailInfo.getOffsetBillType().equals(1)) {
                    //Select DeviceDailyBillPo by offsetBillDetailInfo.getOffsetBillId
                    DeviceDailyBillPo deviceDailyBillPo = deviceDailyBillMapper.selectById(offsetBillDetailInfo.getOffsetBillId());
                    deviceDailyBillPo.setPaymentStatus(PaymentStatusEnum.getByValue(String.valueOf(offsetBillDetailInfo.getOffsetStatus())));
                    deviceDailyBillPo.setPaidAmount(offsetBillDetailInfo.getActualOffsetAmount());
                    deviceDailyBillPo.setUnpaidAmount(deviceDailyBillPo.getUnpaidAmount().subtract(offsetBillDetailInfo.getActualOffsetAmount()));
                    waitOffsetDeviceDailyBillInfoList.add(deviceDailyBillPo);

                } else {
                    //Select ProfitLossRecordPo by offsetBillDetailInfo.getOffsetBillId
                    ProfitLossRecordPo receivableAdjustProfitLossPo = profitLossRecordMapper.selectById(offsetBillDetailInfo.getOffsetBillId());
                    receivableAdjustProfitLossPo.setPaymentStatus(PaymentStatusEnum.getByValue(String.valueOf(offsetBillDetailInfo.getOffsetStatus())));
                    receivableAdjustProfitLossPo.setPaidAmount(offsetBillDetailInfo.getActualOffsetAmount());
                    receivableAdjustProfitLossPo.setUnpaidAmount(receivableAdjustProfitLossPo.getUnpaidAmount().subtract(offsetBillDetailInfo.getActualOffsetAmount()));
                    waitOffsetReceivableAdjustProfitLossInfoList.add(receivableAdjustProfitLossPo);

                }
            }
        }
    }
}
